﻿using System;

namespace WpInsights.Core
{
    using Microsoft.VisualBasic.FileIO;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;

    public static class ExcelConvertor
    {
        private const string UnknowMethod = "UNKNOWN";

        public static List<StackTraceRecord> GetStackTraceRecords(string fullExcelFilePath)
        {
            string fullCsvFilePath = ConvertExcelToCsv(fullExcelFilePath);
            var result = ConvertCsvToObjectModel(fullCsvFilePath);
            File.Delete(fullCsvFilePath);
            return result;
        }


        private static string ConvertExcelToCsv(string fileName)
        {
            string fullInputFileName = Path.GetFullPath(fileName);
            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
            string outputFileName = fileNameWithoutExtension + "2.csv";
            string fileDirectory = new FileInfo(fullInputFileName).Directory.FullName;
            string fullOutputFileName = Path.Combine(fileDirectory, outputFileName);
            ConvertExcelToCsv(fullInputFileName, fullOutputFileName);
            return fullOutputFileName;
        }

        private static void ConvertExcelToCsv(string inputFileName, string outputFileName)
        {
            Type ExcelType = Type.GetTypeFromProgID("Excel.Application");
            dynamic objExcel = Activator.CreateInstance(ExcelType);

            object xlWorkbook = null;

            try
            {
                objExcel.Application.visible = false;
                objExcel.Application.displayalerts = false;

                dynamic objExcelBook = objExcel.Workbooks.Open(inputFileName);
                xlWorkbook = objExcelBook;
                objExcelBook.SaveAs(outputFileName, 23);
            }
            finally
            {
                if (objExcel != null)
                {
                    objExcel.Quit();

                    Marshal.FinalReleaseComObject(objExcel);

                    if (xlWorkbook != null)
                    {
                        Marshal.FinalReleaseComObject(xlWorkbook);
                    }
                }
            }
        }

        public static List<StackTraceRecord> ConvertCsvToObjectModel(string fullCsvFilePath)
        {
            List<StackTraceRecord> resultList = new List<StackTraceRecord>();
          
            using (FileStream reader = File.OpenRead(fullCsvFilePath)) // mind the encoding - UTF8
            {
                using (TextFieldParser parser = new TextFieldParser(reader))
                {
                    parser.TrimWhiteSpace = true; // if you want
                    parser.Delimiters = new[] { "," };
                    parser.HasFieldsEnclosedInQuotes = true;

                    bool isFirstLine = true;
                    while (!parser.EndOfData)
                    {

                        string[] fields = parser.ReadFields();

                        if (isFirstLine)
                        {
                            isFirstLine = false;

                            // headers
                            continue;
                        }


                        StackTraceRecord record = new StackTraceRecord();
                        record.App = fields[0];
                        record.AppId = Guid.Parse(fields[1]);
                        var appVersion = fields[2];
                        if (string.IsNullOrEmpty(appVersion))
                        {
                            // for windows phone 7.0 it is empty
                            appVersion = "1.0.0.0";
                        }

                        record.AppVersion = new Version(appVersion);
                        record.OsVersion = new Version(fields[3]);
                        record.ProblemFunction = fields[4];
                        record.ExceptionType = fields[5];
                        record.CrashCount30Day = Convert.ToInt32(fields[6]);
                        record.StackTrace = ParseStack(fields[7]);
                        resultList.Add(record);
                    }
                }
            }

            return resultList;
        }


        private static StackPointList ParseStack(string stack)
        {
            byte[] byteArray = Encoding.UTF8.GetBytes(stack);
            MemoryStream stream = new MemoryStream(byteArray);

            var resultList = new StackPointList();
            using (TextFieldParser parser = new TextFieldParser(stream))
            {
                parser.TrimWhiteSpace = true; // if you want
                parser.Delimiters = new[] { " " };
                parser.HasFieldsEnclosedInQuotes = false;

                bool isFirstLine = true;
                while (!parser.EndOfData)
                {
                    string[] fields = parser.ReadFields();

                    if (isFirstLine)
                    {
                        isFirstLine = false;

                        // headers
                        continue;
                    }

                    var record = new StackPoint();

                    // for some reason parser has empty fields and does not trim white spaces
                    var removedFields = RemoveEmptyFields(fields);
                    if (removedFields.Count == 2)
                    {
                        // some windows phone 7.1 does not have image and function, better skip such method as it does not
                        // have any information
                        continue;
                    }

                    if (removedFields.Count < 4)
                    {
                        // some windows phone 7.1 stack do not have function name, just image name, skip it.
                        continue;
                    }


                    record.Frame = int.Parse(removedFields[0]);
                    record.Image = removedFields[1];
                    record.Function = removedFields[2];
                    string[] functions = removedFields[2].Split('\n');
                    if (functions.Length > 1)
                    {
                        Console.WriteLine("More than one function in the stack");
                    }

                    record.Offset = (int)new System.ComponentModel.Int32Converter().ConvertFromString(removedFields[3]);
                    resultList.Items.Add(record);
                }
            }
            resultList = RemoveDuplicateUnknowns(resultList);
            return resultList;
        }

        private static StackPointList RemoveDuplicateUnknowns(StackPointList stackPointList)
        {
            StackPointList result = new StackPointList();
            foreach (var stackPoint in stackPointList.Items)
            {
                if (stackPoint.Function == UnknowMethod)
                {
                    
                    if (result.Items.Count > 0)
                    {
                        StackPoint previousMethod = null;
                        previousMethod = result.Items[result.Items.Count - 1];
                        if (previousMethod.Function == UnknowMethod && previousMethod.Image == stackPoint.Image)
                        {
                            continue;
                        }
                    }
                }
                
                result.Items.Add(stackPoint);
            }

            return result;
        }
 
        private static List<string> RemoveEmptyFields(string[] fields)
        {
            var result = new List<string>();
            foreach (var field in fields)
            {
                if (!string.IsNullOrEmpty(field))
                {
                    result.Add(field);
                }
            }

            return result;
        }
    }
}
